#include "plot.h"

#include <vector>
#include <iostream>
#include <sstream>
#include <limits>
#include <math.h>

#include <QGLWidget>
#include <QGLFormat>

#include "Parser.h"

//------------------------------------------------------------------------------

Plot::Plot(QWidget *parent) : QWidget(parent)
{
	scene = new QGraphicsScene();
	view = new QGraphicsView(scene);

	view->setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
	view->setRenderHints(QPainter::Antialiasing);

	drawAxis(-1,1);

	button = new QPushButton(tr("Draw"));

	label = new QLabel(tr("f(x) = "));
	fromLabel = new QLabel(tr("from"));
	toLabel = new QLabel(tr("to"));

	lineEdit = new QLineEdit();
	fromEdit = new QLineEdit();
	toEdit = new QLineEdit();

	fromEdit->setText("-1.0");
	toEdit->setText("1.0");

	fromEdit->setFixedWidth(50);
	toEdit->setFixedWidth(50);

	hLayout = new QHBoxLayout();
	hLayout->addWidget(label);
	hLayout->addWidget(lineEdit);
	hLayout->addWidget(fromLabel);
	hLayout->addWidget(fromEdit);
	hLayout->addWidget(toLabel);
	hLayout->addWidget(toEdit);
	hLayout->addWidget(button);

	mainLayout = new QVBoxLayout;
	mainLayout->addLayout(hLayout);
	mainLayout->addWidget(view);

	setLayout(mainLayout);
	setWindowTitle(tr("Plot"));
	resize(500,550);

	connect(button, SIGNAL(clicked()), this, SLOT(draw()));
}

//------------------------------------------------------------------------------

Plot::~Plot()
{

}

//------------------------------------------------------------------------------

void Plot::draw() {
	string function = lineEdit->text().toStdString();

	double from = fromEdit->text().toDouble();
	double to = toEdit->text().toDouble();

	Parser parser(function);


	vector<double> x = parser.generateX(from,to,0.01);
	vector<double> y;

	try{
		y = parser.getY(x);
	} catch( exception& e ) {
		cout << "BadInputException" << endl;
		return;
	}

	vector<double> diff = parser.getDiff(y);

	double maxX=x[0];
	for(int i =1; i < x.size(); i++) {
		if( x[i] > maxX && x[i] < numeric_limits<double>::max() )
			maxX = x[i];
		if( x[i] < 0 && x[i]*-1 > maxX && x[i] < numeric_limits<double>::max() )
			maxX = -1* x[i];
	}

	double max = 0;
	for(int i =1; i < y.size(); i++) {
		if( y[i] > max && y[i] < maxX*10 && y[i] < numeric_limits<double>::max() )
			max = y[i];
		if( y[i] < 0 && y[i]*-1 > max && y[i]*-1 < maxX*10 && y[i]*-1 < numeric_limits<double>::max() )
			max = -1*y[i];
	}

	double maxD = 0;
	for(int i = 0; i < diff.size(); i++) {
		if( diff[i] > maxD && diff[i] < max/100 && diff[i] < numeric_limits<double>::max() && !isnan(diff[i]) )
			maxD = diff[i];
		if( diff[i] < 0 && diff[i]*-1 > maxD && diff[i]*-1 < max/100 && diff[i]*-1 < numeric_limits<double>::max() && !isnan(diff[i]) )
			maxD = -1*diff[i];
	}

	drawAxis(maxX,max);

	double stepY = 0;
	if(max != 0)
		stepY = height / max;
	double stepX = 0;
	if( maxX != 0 )
		stepX = width / maxX;
	double stepD = 0;
	if( maxD != 0 )
		stepD = height / maxD;

	for( int i = 1; i < x.size()-2; i++ ) {
		QPen pen;
		QColor color;

		color.setRgb(102,153,0);
		pen.setColor(color);
		if( diff[i] != numeric_limits<double>::infinity() && diff[i+1] != numeric_limits<double>::infinity() )
			if( (diff[i] <= maxD+0.0001 && diff[i+1] <= maxD+0.0001) && (diff[i]*-1 <= maxD+0.0001 && diff[i+1]*-1 <= maxD+0.0001) )
				if( !isnan(diff[i]) && !isnan(diff[i+1])) {
					scene->addLine(x[i]*stepX,-1*diff[i]*stepD,x[i+1]*stepX,-1*diff[i+1]*stepD,pen);
				}

		if( y[i] == numeric_limits<double>::infinity() || y[i+1] == numeric_limits<double>::infinity() )
			continue;

		if( y[i] > max || y[i+1] > max || y[i]*-1 > max || y[i+1]*-1 > max )
			continue;

		if( isnan(y[i]) || isnan(y[i+1])) {
			continue;
		}

		color.setRgb(51,102,153);
		pen.setColor(color);

		scene->addLine(x[i]*stepX,-1*y[i]*stepY,x[i+1]*stepX,-1*y[i+1]*stepY,pen);
		scene->addLine(-1,0,1,0);

	}

}

//------------------------------------------------------------------------------

void Plot::drawAxis( double maxX, double maxY) {
	width = 200;
	height = 200;

	scene->clear();

	scene->addLine(-width-(0.1*width),0,width+(0.1*width),0);
	scene->addLine(0,-height-(0.1*height),0,height+(0.1*height));

	for( double i = -10; i <= 10; i++ ) {
		scene->addLine(width*(i/10.0),5,width*(i/10.0),-5);
	}

	for( double i = -10; i <= 10; i++ ) {
		scene->addLine(-5,height*(i/10.0),5,height*(i/10.0));
	}

	QGraphicsTextItem* a = scene->addText(QString("%1").arg(maxX,0,'f',1));
	double w = a->boundingRect().width();
	a->moveBy(width-(w/2.0),5);

	QGraphicsTextItem* b = scene->addText(QString("%1").arg(-maxX,0,'f',1));
	w = b->boundingRect().width();
	b->moveBy(-width-(w/2.0),5);

	QGraphicsTextItem* c = scene->addText(QString("%1").arg(maxY,0,'f',1));
	w = c->boundingRect().height();
	c->moveBy(5,width-(w/2.0));

	QGraphicsTextItem* d = scene->addText(QString("%1").arg(-maxY,0,'f',1));
	w = d->boundingRect().height();
	d->moveBy(5,-width-(w/2.0));

	scene->addLine(-1,0,1,0);
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
